package com.sikhyeon.datastructure.controller;

import com.sikhyeon.datastructure.model.BinarySearchTree;
import com.sikhyeon.datastructure.model.TreeNode;
import javafx.animation.FillTransition;
import javafx.animation.SequentialTransition;
import javafx.animation.TranslateTransition;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Line;
import javafx.scene.text.Text;
import javafx.stage.Stage;
import javafx.util.Duration;
import java.io.IOException;
import java.util.HashSet;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;

public class BinarySearchTreeController {

    @FXML
    private Pane treePane;
    @FXML
    private Button addButton, searchButton, resetButton, backButton;
    @FXML
    private Label statusLabel;

    private BinarySearchTree bst = new BinarySearchTree();
    private Random random = new Random();
    private Set<Integer> values = new HashSet<>();

    @FXML
    public void initialize() {
        addButton.setOnAction(event -> addValue());
        searchButton.setOnAction(event -> searchValue());
        resetButton.setOnAction(event -> resetTree());
        backButton.setOnAction(event -> {
            try {
                goBack();
            } catch (IOException e) {
                e.printStackTrace();
            }
        });
    }

    private void addValue() {
        if (values.size() >= 7) {
            statusLabel.setText("Tree is full.");
            return;
        }

        for (int i = 0; i < 7;i++) {
            int value;
            do {
                value = random.nextInt(100);
            } while (values.contains(value));

            values.add(value);
            bst.insert(value);
        }
        drawTree();
        statusLabel.setText("Tree generated with 15 nodes.");
    }

    private void searchValue() {
        int value = random.nextInt(100);
        SequentialTransition seqTransition = new SequentialTransition();

        AtomicReference<TreeNode> nodeRef = new AtomicReference<>(bst.getRoot());
        boolean[] found = {false}; // effectively final

        while (nodeRef.get() != null) {
            Circle circle = findCircle(nodeRef.get());
            FillTransition ft = new FillTransition(Duration.seconds(1), circle);
            if (nodeRef.get().getValue() == value) {
                found[0] = true;
                ft.setToValue(Color.BLUE);
            } else {
                ft.setToValue(Color.RED);
            }
            seqTransition.getChildren().add(ft);

            if (nodeRef.get().getValue() > value) {
                nodeRef.set(nodeRef.get().getLeft());
            } else {
                nodeRef.set(nodeRef.get().getRight());
            }
        }

        seqTransition.setOnFinished(event -> {
            if (found[0]) {
                statusLabel.setText("Found: " + value);
            } else {
                statusLabel.setText("Not found: " + value);
            }
        });

        seqTransition.play();
    }

    private Circle findCircle(TreeNode node) {
        for (Node n : treePane.getChildren()) {
            if (n instanceof Circle) {
                Text text = (Text) treePane.getChildren().get(treePane.getChildren().indexOf(n) + 1);
                if (Integer.parseInt(text.getText()) == node.getValue()) {
                    return (Circle) n;
                }
            }
        }
        return null;
    }

    private void resetTree() {
        bst.reset();
        values.clear();
        treePane.getChildren().clear();
        statusLabel.setText("Tree reset");
    }

    private void drawTree() {
        treePane.getChildren().clear();
        drawNode(bst.getRoot(), treePane.getWidth() / 2, 100, treePane.getWidth() / 6); // 최초 노드의 Y 값을 100으로 설정
    }

    private void drawNode(TreeNode node, double x, double y, double hGap) {
        if (node == null) {
            return;
        }
        if (node.getLeft() != null) {
            drawLine(x - hGap, y + 50, x, y ); // 선의 Y 값을 조정
            drawNode(node.getLeft(), x - hGap, y + 50, hGap /2 );
        }
        if (node.getRight() != null) {
            drawLine(x + hGap, y + 50, x, y ); // 선의 Y 값을 조정
            drawNode(node.getRight(), x + hGap, y + 50, hGap / 2);
        }
        drawCircle(x, y, String.valueOf(node.getValue()));
    }

    private void drawCircle(double x, double y, String value) {
        Circle circle = new Circle(x, y, 15);
        circle.setFill(Color.WHITE);
        circle.setStroke(Color.BLACK);
        Text text = new Text(x - 5, y + 5, value);
        treePane.getChildren().addAll(circle, text);

        TranslateTransition transition = new TranslateTransition(Duration.seconds(0.5), circle);
        transition.setFromY(y - 30);
        transition.setToY(y);
        transition.play();

        TranslateTransition textTransition = new TranslateTransition(Duration.seconds(0.5), text);
        textTransition.setFromY(y - 30);
        textTransition.setToY(y);
        textTransition.play();
    }

    private void drawLine(double x1, double y1, double x2, double y2) {
        Line line = new Line(x1, y1, x2, y2);
        treePane.getChildren().add(line);
    }



    public void goBack() throws IOException {
        FXMLLoader loader = new FXMLLoader(getClass().getResource("/com/sikhyeon/datastructure/view/MainView.fxml"));
        Parent root = loader.load();
        Stage stage = (Stage) treePane.getScene().getWindow();
        stage.setScene(new Scene(root));
        stage.show();
    }
}
